package com.filyar.htyd.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.filyar.htyd.entity.Az;
import com.filyar.htyd.entity.CompareResult;
import com.filyar.htyd.entity.CompareTask;
import com.filyar.htyd.entity.User;
import com.filyar.htyd.mapper.AzMapper;
import com.filyar.htyd.mapper.CompareResultMapper;
import com.filyar.htyd.mapper.CompareTaskMapper;
import com.filyar.htyd.service.IAzService;
import com.filyar.htyd.service.ICompareResultService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author filyar
 * @e-mail root@filyar.com
 * @since 2024-10-24
 */
@Service
public class AzServiceImpl extends ServiceImpl<AzMapper, Az> implements IAzService {

    @Resource
    private AzMapper azMapper;
    @Resource
    private CompareTaskMapper compareTaskMapper;
    @Resource
    private ICompareResultService compareResultService;
    @Resource
    private CompareResultMapper compareResultMapper;

    @Override
    @Async("async-executor")
    public void compare() {
        compareResultMapper.truncateTable();
        ArrayList<CompareResult> compareResults = new ArrayList<>();
        List<Az> azs = azMapper.selectList(null);
        DateTime executionStartTime = DateTime.now();
        String serialNumber = DateUtil.now() + "---" + IdUtil.fastSimpleUUID();

        CompareTask compareTask = new CompareTask();
        compareTask.setExecutionCreateTime(executionStartTime);
        compareTask.setSerialNumber(serialNumber);
        compareTask.setExecutionStatus(0);
//        compareTask.setOperatorName((JSONObject.parseObject(JSONObject.toJSONString(StpUtil.getSession().get("user")), User.class)).getNickname());
        compareTask.setExecutionProgress(0);
        compareTaskMapper.insert(compareTask);
        int azSize = azs.size();
        int chunkSize = azSize / 100;
        for (int i = 0; i < azSize; i++) {
            if (i % chunkSize == 0) {
                DateTime loopTime = DateTime.now();
                compareTask.setExecutionEndTime(loopTime);
                compareTask.setExecutionTime(DateUtil.between(executionStartTime, loopTime, DateUnit.SECOND));
                compareTask.setExecutionProgress(compareTask.getExecutionProgress() + 1);
                compareTaskMapper.updateById(compareTask);
            }
            String currentBigLoopA = azs.get(i).getA();
            String currentBigLoopZ = azs.get(i).getZ();
            HashSet<String> currentAHashSet = new HashSet<>(Arrays.asList(currentBigLoopA.split("")));
            HashSet<String> currentZHashSet = new HashSet<>(Arrays.asList(currentBigLoopZ.split("")));
            for (int j = i + 1; j < azSize; j++) {
                String secondLoopA = azs.get(j).getA();
                String secondLoopZ = azs.get(j).getZ();
                String[] charsA2 = secondLoopA.split("");
                String[] charsZ2 = secondLoopZ.split("");
                int likeA = 0;
                for (String charA2 : charsA2) {
                    if (currentAHashSet.contains(charA2))
                        likeA++;
                }
                int likeZ = 0;
                for (String charZ2 : charsZ2) {
                    if (currentZHashSet.contains(charZ2))
                        likeZ++;
                }

                int maxA = Math.max(currentBigLoopA.length(), secondLoopA.length());
                int maxZ = Math.max(currentBigLoopZ.length(), secondLoopZ.length());

                double aLikePercent = 0;
                double zLikePercent = 0;
                if (likeA == 0) {
                    aLikePercent = 0;
                } else {
                    aLikePercent = Double.parseDouble(new DecimalFormat("#.##").format(likeA / (double) maxA));
                }
                if (likeZ == 0) {
                    zLikePercent = 0;
                } else {
                    zLikePercent = Double.parseDouble(new DecimalFormat("#.##").format(likeZ / (double) maxZ));
                }

                if (aLikePercent >= 0.5 && zLikePercent >= 0.5) {
                    CompareResult compareResult = new CompareResult();
                    compareResult.setA(currentBigLoopA);
                    compareResult.setZ(currentBigLoopZ);
                    compareResult.setCompareA(secondLoopA);
                    compareResult.setCompareZ(secondLoopZ);
                    compareResult.setALikeCount(likeA);
                    compareResult.setZLikeCount(likeZ);
                    compareResult.setALikePercent(aLikePercent);
                    compareResult.setZLikePercent(zLikePercent);
                    compareResult.setCreateTime(executionStartTime);
                    compareResult.setSerialNumber(serialNumber);
                    compareResults.add(compareResult);
                }
            }
        }
        DateTime executionFinishTime = DateTime.now();
        compareTask.setExecutionEndTime(executionFinishTime);
        compareTask.setExecutionTime(DateUtil.between(executionStartTime, executionFinishTime, DateUnit.SECOND));
        compareTask.setExecutionStatus(1);
        compareTask.setExecutionProgress(100);
        compareTaskMapper.updateById(compareTask);
        //分batch保存
        int batchSize = compareResults.size() / 100;
        ArrayList<List<CompareResult>> batches = new ArrayList<>();
        int compareResultSize = compareResults.size();
        for (int i = 0; i < compareResultSize; i += batchSize) {
            batches.add(compareResults.subList(i, Math.min(i + batchSize, compareResultSize)));
        }
        int core = Runtime.getRuntime().availableProcessors();
        ExecutorService executorService = Executors.newFixedThreadPool(core);
        AtomicInteger completedTasks = new AtomicInteger(0); // 记录完成的任务数
        try {
            compareTask.setSaveProgress(0);
            compareTask.setSaveStartTime(executionFinishTime);
            compareTask.setSaveStatus(0);
            compareTaskMapper.updateById(compareTask);

            for (int i = 0; i < batches.size(); i++) {
                final int batchIndex = i; // 使用局部变量
                executorService.execute(() -> {
                    compareResultService.saveBatch(batches.get(batchIndex));
                    int completed = completedTasks.incrementAndGet();

                    // 每完成10个任务或最后一个任务时更新进度
                    if (completed % 10 == 0 || batchIndex == batches.size() - 1) {
                        synchronized (compareTask) {
                            compareTask.setSaveProgress(completed);

                            // 更新状态
                            if (batchIndex == batches.size() - 1) {
                                compareTask.setSaveStatus(1); // 设置为完成状态
                                compareTask.setSaveProgress(100); // 更新为100%
                                DateTime saveEndTime = DateTime.now();
                                compareTask.setSavaEndTime(saveEndTime);
                                compareTask.setSaveTime(DateUtil.between(executionFinishTime, saveEndTime, DateUnit.SECOND));
                            }
                            compareTaskMapper.updateById(compareTask);
                        }
                    }
                    System.out.println("批次处理完成：" + batches.get(batchIndex));
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            executorService.shutdown();
            try {
                if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                    executorService.shutdownNow(); // 超时则强制关闭
                }
            } catch (InterruptedException e) {
                executorService.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }

    }

    @Override
    public List<CompareResult> check(Az az) {
        ArrayList<CompareResult> compareResults = new ArrayList<>();
        String a = az.getA();
        String z = az.getZ();
        HashSet<String> currentLoopAHashSet = new HashSet<>(Arrays.asList(a.split("")));
        HashSet<String> currentLoopZHashSet = new HashSet<>(Arrays.asList(z.split("")));
        List<Az> azs = azMapper.selectList(null);
        for (Az singleAz : azs) {
            String currentLoopA = singleAz.getA();
            String currentLoopZ = singleAz.getZ();
            CompareResult compareResult = getCompareResult(currentLoopA, currentLoopZ, a, z, currentLoopAHashSet, currentLoopZHashSet);
            if (compareResult != null){
                compareResults.add(compareResult);
            }
        }
        return compareResults;
    }

    private CompareResult getCompareResult(String currentLoopA,
                                           String currentLoopZ,
                                           String inputA,
                                           String inputZ,
                                           HashSet<String> inputAHashSet,
                                           HashSet<String> inputZHashSet) {
        String[] charsA2 = currentLoopA.split("");
        String[] charsZ2 = currentLoopZ.split("");
        int likeA = 0;
        for (String charA2 : charsA2) {
            if (inputAHashSet.contains(charA2))
                likeA++;
        }
        int likeZ = 0;
        for (String charZ2 : charsZ2) {
            if (inputZHashSet.contains(charZ2))
                likeZ++;
        }
        int maxA = Math.max(currentLoopA.length(), inputAHashSet.size());
        int maxZ = Math.max(currentLoopZ.length(), inputZHashSet.size());

        double aLikePercent = 0;
        double zLikePercent = 0;
        if (likeA == 0) {
            aLikePercent = 0;
        } else {
            aLikePercent = Double.parseDouble(new DecimalFormat("#.##").format(likeA / (double) maxA));
        }
        if (likeZ == 0) {
            zLikePercent = 0;
        } else {
            zLikePercent = Double.parseDouble(new DecimalFormat("#.##").format(likeZ / (double) maxZ));
        }

        if (aLikePercent >= 0.5 && zLikePercent >= 0.5) {
            CompareResult compareResult = new CompareResult();
            compareResult.setA(inputA);
            compareResult.setZ(inputZ);
            compareResult.setCompareA(currentLoopA);
            compareResult.setCompareZ(currentLoopZ);
            compareResult.setALikeCount(likeA);
            compareResult.setZLikeCount(likeZ);
            compareResult.setALikePercent(aLikePercent);
            compareResult.setZLikePercent(zLikePercent);
            return compareResult;
        } else {
            return null;
        }
    }
}
